﻿using System.Collections.Generic;
using Microsoft.SqlServer.Dts.Pipeline;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

namespace DatabaseGroup.SSIS.SourceAdapters
{
    /// <summary>
    /// Holds property information about an output column
    /// </summary>
    internal struct ColumnInfo
    {
        /// <summary>
        /// The index of the column in the output buffer
        /// </summary>
        public int BufferColumnIndex;

        /// <summary>
        /// The name of the column
        /// </summary>
        public string Name;

        /// <summary>
        /// The DataType of the column
        /// </summary>
        public DataType DataType;

        /// <summary>
        /// The length of the column
        /// </summary>
        public int Length;

        /// <summary>
        /// The ID of the column
        /// </summary>
        public int ID;

        /// <summary>
        /// The error row disposition setting of the column
        /// </summary>
        public DTSRowDisposition ErrorRowDisposition;

        /// <summary>
        /// The truncation row disposition setting of the column
        /// </summary>
        public DTSRowDisposition TruncationRowDisposition;
    }

    /// <summary>
    /// Caches the column properties of an output at runtime
    /// </summary>
    /// <remarks>
    /// When doing any sort of component metadata or buffer data
    /// property lookups you find it takes quite a long time (relative),
    /// so caching the values for fast lookup helps in the processing.
    /// </remarks>
    internal class OutputRuntimePropertiesCache
    {
        #region Data members

        private readonly IDTSOutput100 _output;
        private readonly IDTSBufferManager100 _bufferManager;
        private List<ColumnInfo> _columnInfoCache;
        private Dictionary<string, int> _columnIndexLookup;
        
        #endregion
        
        /// <summary>
        /// Initializes a new instance of OutputRuntimePropertiesCache
        /// using the output and associated buffer manager
        /// </summary>
        /// <param name="output">The output containing the column properties to cache</param>
        /// <param name="bufferManager">The buffer manager associated with the output</param>
        internal OutputRuntimePropertiesCache(IDTSOutput100 output, IDTSBufferManager100 bufferManager)
        {
            _output = output;
            _bufferManager = bufferManager;

            OutputID = output.ID;

            InitializeCache();
        }

        /// <summary>
        /// Gets the ID of the cached output
        /// </summary>
        internal int OutputID { get; private set; }

        /// <summary>
        /// Gets or sets the buffer associated with the output
        /// </summary>
        internal PipelineBuffer Buffer { get; set; }

        /// <summary>
        /// Gets the number of columns currently within the cache
        /// </summary>
        internal int Count
        {
            get
            {
                return _columnInfoCache.Count;
            }
        }

        /// <summary>
        /// Caches the output column properties using the
        /// current output and buffer objects
        /// </summary>
        internal void InitializeCache()
        {
            _columnInfoCache = new List<ColumnInfo>();
            _columnIndexLookup = new Dictionary<string, int>();

            if (_output == null)
                return;

            IDTSOutputColumnCollection100 outputColumns = _output.OutputColumnCollection;

            int columnIndex = 0;
            foreach (IDTSOutputColumn100 outputColumn in outputColumns)
            {
                ColumnInfo colInfo = new ColumnInfo();
                if (_output.Buffer > 0)
                    colInfo.BufferColumnIndex = _bufferManager.FindColumnByLineageID(_output.Buffer, outputColumn.LineageID);
                colInfo.ID = outputColumn.ID;
                colInfo.DataType = outputColumn.DataType;
                colInfo.Length = outputColumn.Length;
                colInfo.Name = outputColumn.Name;
                colInfo.ErrorRowDisposition = outputColumn.ErrorRowDisposition;
                colInfo.TruncationRowDisposition = outputColumn.TruncationRowDisposition;
                _columnInfoCache.Add(colInfo);
                _columnIndexLookup.Add(colInfo.Name, columnIndex++);
            }
        }

        /// <summary>
        /// Determines if the cache contains a certain column name
        /// </summary>
        /// <param name="columnName">The column name to search for</param>
        /// <returns>True if the cache contains the column name</returns>
        internal bool Contains(string columnName)
        {
            return _columnIndexLookup.ContainsKey(columnName);
        }

        /// <summary>
        /// Gets the column at the zero-based index within the cache
        /// </summary>
        /// <param name="columnIndex">The zero-based index to lookup</param>
        /// <returns>The ColumnInfo at the specified index</returns>
        internal ColumnInfo this[int columnIndex]
        {
            get
            {
                return _columnInfoCache[columnIndex];
            }
        }

        /// <summary>
        /// Gets the column based on the specified column name
        /// </summary>
        /// <param name="columnName">The column name to retrieve column info on</param>
        /// <returns>The ColumnInfo for the specified column name</returns>
        internal ColumnInfo this[string columnName]
        {
            get
            {
                return _columnInfoCache[_columnIndexLookup[columnName]];
            }
        }
    }
}
